﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Timers;

namespace ThunderSDKDemo
{
    /// <summary>
    /// 迅雷下载管理器
    /// </summary>
    public class ThunderDownLoadCore
    {
        public static readonly ThunderDownLoadCore Instance = new ThunderDownLoadCore();

        /// <summary>
        /// 下载任务监测定时器
        /// </summary>
        private System.Timers.Timer MonitorTaskTimer { set; get; }

        #region 下载任务进度监测

        /// <summary>
        /// 下载任务监测
        /// </summary>
        private class ThunderTaskMonitor
        {
            /// <summary>
            /// 任务名称
            /// </summary>
            public string TaskName { set; get; }

            public IntPtr TaskHandle { set; get; }

            /// <summary>
            /// 任务是否完成
            /// </summary>
            public bool IsComplete { set; get; }

            /// <summary>
            /// 任务出错
            /// </summary>
            public bool IsError { set; get; }

            /// <summary>
            /// 是否已经开启任务
            /// </summary>
            public bool IsStartTask { set; get; }
        }

        /// <summary>
        /// 任务监测列表
        /// </summary>
        private Dictionary<string, ThunderTaskMonitor> DownLoadTaskMonitor = new Dictionary<string, ThunderTaskMonitor>();

        /// <summary>
        /// 任务监测的读写锁
        /// </summary>
        private static readonly object MonitorLock = new object();

        /// <summary>
        /// 是否开启任务监测
        /// </summary>
        private bool IsStartMonitor = false;

        #endregion

        #region 事件

        /// <summary>
        /// 下载进度信息通知事件
        /// </summary>
        public event EventHandler<ThunderProgressInfo> ProgressChangedEvent;

        /// <summary>
        /// 发送进度信息
        /// </summary>
        private void ShowProgressInfo(ThunderProgressInfo progressInfo)
        {
            ProgressChangedEvent?.Invoke(this, progressInfo);
        }

        #endregion

        /// <summary>
        /// 是否初始化迅雷下载引擎
        /// </summary>
        private bool IsInitThunderEngine { set; get; } = false;

        /// <summary>
        /// 是否退出下载任务
        /// </summary>
        private bool IsCloseTask { set; get; } = false;

        private ThunderDownLoadCore()
        {
            IsInitThunderEngine = ThunderEngine.NativeMethods.XL_Init();

            MonitorTaskTimer = new System.Timers.Timer(500)
            {
                Enabled = false,
                AutoReset = true,
                Interval = 500,
            };
            MonitorTaskTimer.Elapsed += MonitorTaskTimer_Elapsed;
        }

        /// <summary>
        /// 关闭下载任务
        /// </summary>
        public void Close()
        {
            IsCloseTask = true;

            lock (MonitorLock)
            {
                foreach (string key in DownLoadTaskMonitor.Keys)
                {
                    if (DownLoadTaskMonitor[key].IsStartTask)
                    {
                        ThunderEngine.NativeMethods.XL_StopTask(DownLoadTaskMonitor[key].TaskHandle);
                    }
                }

                DownLoadTaskMonitor.Clear();
            }

            MonitorTaskTimer.Stop();
            MonitorTaskTimer.Dispose();

            ThunderEngine.NativeMethods.XL_UnInit();
        }

        /// <summary>
        /// 定时器监测下载任务
        /// </summary>
        private void MonitorTaskTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (DownLoadTaskMonitor.Count > 0)
            {
                lock (MonitorLock)
                {
                    foreach (string key in DownLoadTaskMonitor.Keys)
                    {
                        if (IsCloseTask)
                        {
                            return;
                        }

                        if (DownLoadTaskMonitor[key].IsStartTask
                            && !DownLoadTaskMonitor[key].IsComplete
                            && !DownLoadTaskMonitor[key].IsError)
                        {
                            ThunderEngine.DownTaskInfo taskInfo = new ThunderEngine.DownTaskInfo();
                            if (ThunderEngine.NativeMethods.XL_QueryTaskInfoEx(DownLoadTaskMonitor[key].TaskHandle, taskInfo))
                            {
                                switch (taskInfo.stat)
                                {
                                    case ThunderEngine.DOWN_TASK_STATUS.TSC_COMPLETE:
                                        {
                                            DownLoadTaskMonitor[key].IsComplete = true;

                                            /*下载完成*/
                                            ShowProgressInfo(new ThunderProgressInfo()
                                            {
                                                TaskName = DownLoadTaskMonitor[key].TaskName,
                                                Status = DownLoadTaskStatus.Finish,
                                                CurrentPercent = taskInfo.fPercent,
                                                Speed = taskInfo.nSpeed,
                                                TotalDownload = taskInfo.nTotalDownload,
                                                TotalSize = taskInfo.nTotalSize,
                                            });
                                        }
                                        break;
                                    case ThunderEngine.DOWN_TASK_STATUS.TSC_DOWNLOAD:
                                        {
                                            ShowProgressInfo(new ThunderProgressInfo()
                                            {
                                                TaskName = DownLoadTaskMonitor[key].TaskName,
                                                Status = DownLoadTaskStatus.DownLoading,
                                                CurrentPercent = taskInfo.fPercent,
                                                Speed = taskInfo.nSpeed,
                                                TotalDownload = taskInfo.nTotalDownload,
                                                TotalSize = taskInfo.nTotalSize,
                                            });
                                        }
                                        break;
                                    case ThunderEngine.DOWN_TASK_STATUS.TSC_PAUSE:
                                        {
                                            ShowProgressInfo(new ThunderProgressInfo()
                                            {
                                                TaskName = DownLoadTaskMonitor[key].TaskName,
                                                Status = DownLoadTaskStatus.Pause,
                                                CurrentPercent = taskInfo.fPercent,
                                                Speed = taskInfo.nSpeed,
                                                TotalDownload = taskInfo.nTotalDownload,
                                                TotalSize = taskInfo.nTotalSize,
                                            });
                                        }
                                        break;
                                    case ThunderEngine.DOWN_TASK_STATUS.TSC_ERROR:
                                        {
                                            DownLoadTaskMonitor[key].IsError = true;
                                            DownLoadTaskMonitor[key].IsComplete = true;

                                            ShowProgressInfo(new ThunderProgressInfo()
                                            {
                                                TaskName = DownLoadTaskMonitor[key].TaskName,
                                                Status = DownLoadTaskStatus.Error,
                                                ErrorInfo = GetDownLoadError(taskInfo.fail_code),
                                                CurrentPercent = taskInfo.fPercent,
                                                Speed = taskInfo.nSpeed,
                                                TotalDownload = taskInfo.nTotalDownload,
                                                TotalSize = taskInfo.nTotalSize,
                                            });
                                        }
                                        break;
                                }
                            }
                        }
                    }

                    /*删除已完成的任务监控信息*/
                    List<string> keys = DownLoadTaskMonitor.Keys.ToList();
                    keys.ForEach(child =>
                    {
                        if (DownLoadTaskMonitor[child].IsComplete)
                        {
                            DownLoadTaskMonitor.Remove(child);
                        }
                    });

                    /*不存在未完成的任务，停止定时器*/
                    if (!DownLoadTaskMonitor.Values.ToList().Exists(s => { return !s.IsComplete && !s.IsError && s.IsStartTask; }))
                    {
                        (sender as System.Timers.Timer).Enabled = false;
                    }
                }
            }
        }

        /// <summary>
        /// 根据错误码获取错误信息
        /// </summary>
        /// <param name="fail_code"></param>
        /// <returns></returns>
        private string GetDownLoadError(ThunderEngine.TASK_ERROR_TYPE fail_code)
        {
            string result = "";

            switch (fail_code)
            {
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_CANCEL:
                    result = "任务取消";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_CREATE:
                    result = "创建文件失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_DELETE:
                    result = "删除文件失败失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_FILEHASH:
                    result = "文件全文校验失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_PIECEHASH:
                    result = "文件片校验失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_READ:
                    result = "读文件失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_RENAME:
                    result = "重命名失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DISK_WRITE:
                    result = "写文件失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_DOWN_INVALID:
                    result = "无效的DOWN地址";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_HTTPMGR_NOT_IP:
                    result = "http下载中无ip可用";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_ID_INVALID:
                    result = "TaskId 非法";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_PROXY_AUTH_TYPE_FAILED:
                    result = "代理认证失败";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_PROXY_AUTH_TYPE_UNKOWN:
                    result = "代理类型未知";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_TIMEOUT:
                    result = "任务超时";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_TP_CRASHED:
                    result = "MINITP崩溃";
                    break;
                case ThunderEngine.TASK_ERROR_TYPE.TASK_ERROR_UNKNOWN:
                    result = "未知错误";
                    break;
            }

            return $"{result}，错误码：{fail_code}";

        }

        /// <summary>
        /// 创建下载任务
        /// </summary>
        /// <param name="taskParam"></param>
        /// <param name="error"></param>
        /// <returns></returns>
        public bool CreateThunderTask(ThunderDownTaskParam taskParam, out string error)
        {
            bool result = false;
            error = "";

            ThunderEngine.DownTaskParam task = new ThunderEngine.DownTaskParam()
            {
                IsResume = taskParam.IsResume ? 1 : 0,
                szTaskUrl = taskParam.TaskUrl,
                szFilename = string.IsNullOrEmpty(taskParam.FileName) ? Path.GetFileName(taskParam.TaskUrl) : CorrectFilePath(taskParam.FileName),
                szSavePath = CorrectFilePath(taskParam.SavePath)
            };

            ThunderTaskMonitor taskMonitor = new ThunderTaskMonitor()
            {
                TaskName = taskParam.TaskName,
                TaskHandle = ThunderEngine.NativeMethods.XL_CreateTask(task),
                IsComplete = false,
            };

            if (taskParam.IsDownLoadNow)
            {
                result = ThunderEngine.NativeMethods.XL_StartTask(taskMonitor.TaskHandle);
                taskMonitor.IsStartTask = result;
            }

            if (result)
            {
                DownLoadTaskMonitor.Add(taskMonitor.TaskName, taskMonitor);

                if (null != MonitorTaskTimer && !MonitorTaskTimer.Enabled)
                {
                    MonitorTaskTimer.Start();
                }
            }
            else
            {
                error = "开启下载任务失败！";
            }

            return result;
        }

        /// <summary>
        /// 使用正则表达式修正文件路径
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public string CorrectFilePath(string fileName)
        {
            if (!string.IsNullOrWhiteSpace(fileName))
            {
                Regex regex = new Regex(@" ^[^\/\:\*\?\""\<\>\|\,]+$");
                return regex.Replace(fileName, "_");
            }
            else
            {
                return "";
            }
        }
    }

    /// <summary>
    /// 迅雷下载进度信息
    /// </summary>
    public class ThunderProgressInfo
    {
        /// <summary>
        /// 任务名称
        /// </summary>
        public string TaskName { set; get; }

        /// <summary>
        /// 下载进度类型
        /// </summary>
        public DownLoadTaskStatus Status { set; get; }

        /// <summary>
        /// 下载错误信息
        /// </summary>
        public string ErrorInfo { set; get; }

        /// <summary>
        /// 该任务总大小(字节)
        /// </summary>
        public long TotalSize { set; get; }

        /// <summary>
        /// 下载有效字节数(可能存在回退的情况)
        /// </summary>
        public long TotalDownload { set; get; }

        /// <summary>
        /// 即时速度(字节/秒)
        /// </summary>
        public int Speed { set; get; }

        /// <summary>
        /// 当前进度，百分比
        /// </summary>
        public float CurrentPercent { set; get; }
    }

}
